﻿using System.Collections.Generic;

namespace Godot.core
{
    using SysBindingFlags = System.Reflection.BindingFlags;
    using SysMethodInfo = System.Reflection.MethodInfo;

    public partial class Resource : Reference 
    {
        internal StringName _name;
        internal string _path_cache;

        public Resource()
        {

        }

        public void set_path(string p_path) 
        {
	        if (_path_cache == p_path)
		        return;
		
	        if (_path_cache != "") 
            {	
		        ResourceCache.__resources.Remove(_path_cache);
	        }

	        _path_cache = "";
	        if(ResourceCache.__resources.ContainsKey(p_path) )
                return;

	        _path_cache = p_path;
	
	        if (_path_cache != "") 
            {	
		        ResourceCache.__resources[_path_cache] = this;;
	        }

	        _change_notify("resource/path");
	        _resource_path_changed();	
        }

        public string get_path()
        {	
	        return _path_cache;
        }

        public void set_name(string p_name) 
        {
	        _name = p_name;
	        _change_notify("resource/name");
        }

        public string get_name()
        {
	        return _name;
        }

        public virtual RID get_rid() // some resources may offer conversion to RID
        {
            return new RID();
        }

        public Resource duplicate(bool p_subresources = false)
        {
            List<PropertyInfo> plist = new List<PropertyInfo>();
	        get_property_list(ref plist);


	        Resource r = (Resource)ObjectTypeDB.instance(get_type());
	        if(r == null)
                return new Resource();

	        foreach(PropertyInfo E in plist) 
            {
		        if ((E.usage & (int)PropertyUsageFlags.PROPERTY_USAGE_STORAGE) == 0)
			        continue;

                bool valid = false;
		        Variant p = get(E.name, ref valid);
		        if (p.type == Variant.Type.OBJECT && p_subresources) 
                {
			        Resource sr = (Resource)p;
			        if (sr != null)
				        p = sr.duplicate(true);
		        }

		        r.set(E.name, p, ref valid);
	        }

	        return r;
        }

        public ResourceImportMetadata get_import_metadata() 
        {
	        return new ResourceImportMetadata();
        }

        public void set_import_metadata(ResourceImportMetadata p_metadata) {}

        protected virtual void _resource_path_changed() {}

        protected static new void _bind_methods()
        {
            ObjectTypeDB.bind_method(_MD("set_path","path"), typeof(Resource).GetMethod("set_path", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_path"), typeof(Resource).GetMethod("get_path", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("set_name","name"),typeof(Resource).GetMethod("set_name", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_name"),typeof(Resource).GetMethod("get_name", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_rid"),typeof(Resource).GetMethod("get_rid", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("set_import_metadata","metadata"),typeof(Resource).GetMethod("set_import_metadata", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_import_metadata"),typeof(Resource).GetMethod("get_import_metadata", Misc.BindingFlags_Greedy));

	        ObjectTypeDB.bind_method(_MD("duplicate"), typeof(Resource).GetMethod("duplicate", Misc.BindingFlags_Greedy), new Variant(false));
	        ObjectTypeDB.add_signal(get_type_static(), new MethodInfo("changed"));
	        ObjectTypeDB.add_property(get_type_static(),
                new PropertyInfo(Variant.Type.STRING,"resource/path", PropertyHint.PROPERTY_HINT_NONE, "", (int)PropertyUsageFlags.PROPERTY_USAGE_EDITOR), 
                "set_path", "get_path");
            ObjectTypeDB.add_property(get_type_static(), new PropertyInfo(Variant.Type.STRING, "resource/name").added_usage(PropertyUsageFlags.PROPERTY_USAGE_STORE_IF_NONZERO), "set_name", "get_name");
        }
    }

    public partial class Resource
    {
        #region OBJ_TYPE( Resource, Reference )
        public override string get_type() { return "Resource"; }

        public override string get_save_type() { return get_type(); } //type stored when saving

        public override StringName get_type_name() { return (StringName)get_type_static(); }

        public override bool is_type(string p_type) { return (p_type == "Resource") ? true : base.is_type(p_type); }

        protected override void _initialize_typev() { initialize_type(); }

        protected override bool _setv(StringName p_name, Variant p_property)
        {
            if (base._setv(p_name, p_property))
                return true;

            if (typeof(Resource).GetMethod("_set") != typeof(Reference).GetMethod("_set"))
            {
                return _set(p_name, p_property);
            }

            return false;
        }

        protected override bool _getv(StringName p_name, ref Variant r_property)
        {
            if (typeof(Resource).GetMethod("_get") != typeof(Reference).GetMethod("_get"))
            {
                if (_get(p_name, ref r_property))
                    return true;
            }

            return base._getv(p_name, ref r_property);
        }

        protected override void _get_property_listv(ref List<PropertyInfo> p_list, bool p_reversed)
        {
            if (!p_reversed)
            {
                base._get_property_listv(ref p_list, p_reversed);
            }

            p_list.Add(new PropertyInfo(Variant.Type.NIL, get_type_static(), PropertyHint.PROPERTY_HINT_NONE, "", (int)PropertyUsageFlags.PROPERTY_USAGE_CATEGORY));

            if (!_is_gpl_reversed())
                ObjectTypeDB.get_property_list("Resource", ref p_list, true);

            if (typeof(Resource).GetMethod("_get_property_list") != typeof(Reference).GetMethod("_get_property_list"))
            {
                _get_property_list(ref p_list);
            }

            if (_is_gpl_reversed())
                ObjectTypeDB.get_property_list("Resource", ref p_list, true);

            if (p_reversed)
            {
                base._get_property_listv(ref p_list, p_reversed);
            }
        }

        protected override void _notificationv(int p_notification, bool p_reversed)
        {
            if (!p_reversed)
                base._notificationv(p_notification, p_reversed);

            if (typeof(Resource).GetMethod("_notification") != typeof(Reference).GetMethod("_notification"))
            {
                _notification(p_notification);
            }

            if (p_reversed)
                base._notificationv(p_notification, p_reversed);
        }

        public static new string get_type_static() { return "Resource"; }

        public static new string get_parent_type_static() { return Reference.get_type_static(); }

        public static new void get_inheritance_list_static(ref List<string> r_inheritance_list)
        {
            Reference.get_inheritance_list_static(ref r_inheritance_list);
            r_inheritance_list.Add(get_type_static());
        }

        public static new string get_category_static()
        {
            string category = Reference.get_category_static();
            if (typeof(Resource).GetMethod("_get_category") != typeof(Reference).GetMethod("_get_category"))
            {
                if (category != "")
                    category += "/";
                category += _get_category();
            }
            return category;
        }

        public static new string inherits_static() { return "Reference"; }

        public static new void get_valid_parents_static(ref List<string> p_parents)
        {
            if (typeof(Resource).GetMethod("_get_valid_parents_static") != typeof(Reference).GetMethod("_get_valid_parents_static"))
            {
                Resource._get_valid_parents_static(ref p_parents);
            }

            Reference.get_valid_parents_static(ref p_parents);
        }

        public static new void initialize_type()
        {
            if (__initialized)
                return;
            Reference.initialize_type();
            ObjectTypeDB._add_type<Resource>();

            SysMethodInfo cls_bind = typeof(Resource).GetMethod("_bind_methods", Misc.BindingFlags_Greedy);
            SysMethodInfo par_bind = typeof(Reference).GetMethod("_bind_methods", Misc.BindingFlags_Greedy);
            if (cls_bind != par_bind)
                cls_bind.Invoke(null, null);
            __initialized = true;
        }

        private static bool __initialized = false;
        #endregion

        #region OBJ_CATEGORY("Resources")
        protected static string _get_category() 
        { 
            return "Resources"; 
        }
        #endregion

        #region RES_BASE_EXTENSION("res");
        public static new void register_custom_data_to_otdb() 
        { 
            ObjectTypeDB.add_resource_base_extension("res",get_type_static()); 
        }
        
        public virtual string get_base_extension()
        { 
            return "res";
        }
        #endregion
    }
}
